#include "ad9910.h"
#include "debug.h"
u32 AD_RAM_Data[4096] = {
    0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000,  //
    0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000,  //
    0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000,  //
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,                                                  //
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00                                                   //
};
const TCFR1 c1 = {.Inverse_sinc = ON, .spi_wires = THREE_WIRES};                                     // cfr1控制字 SDIO,反Sinc滤波
const TCFR2 c2 = {0};                                                                                // cfr2控制字 使能单音,关闭SYNC_CLK,关闭PDCLK,
const TCFR3 c3 = {
    .drv0 = Disabled,
    .vco = _920_1030,
    .icp = _387,
    .refClk_bypass = ENABLE,
    .refClk_Diver = ENABLE,
    .PFDReset = ON,
    .PLL_Enable = ENABLE,
    .multiplier = PLL_MULT,
};

static void IO_Init (void) {
    GPIO_InitTypeDef init = {0};
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
    init.GPIO_Pin = AD_PORTA_IN_PINS;
    init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init (AD_PORTA, &init);

    init.GPIO_Pin = AD_PORTB_IN_PINS;
    init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init (AD_PORTB, &init);
    AD_DRDir (0);
    AD_DRHold (0);
    AD_Reset (0);
    init.GPIO_Pin = AD_PORTA_OUT_PINS;
    init.GPIO_Mode = GPIO_Mode_Out_PP;
    init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (AD_PORTA, &init);
    AD_PWR (1);
    AD_Update (0);
    AD_Profile (0);
    AD_OSK (0);
    AD_CS (1);
    AD_CLK (1);
    AD_SDIO (1);
    init.GPIO_Pin = AD_PORTB_OUT_PINS;
    init.GPIO_Mode = GPIO_Mode_Out_PP;
    init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (AD_PORTB, &init);
}

static void Spi_Init (void) {
    SPI_InitTypeDef init = {0};
    AD_SPI_RCC_ENABLE();
    init.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    init.SPI_Mode = SPI_Mode_Master;
    init.SPI_DataSize = SPI_DataSize_8b;
    init.SPI_CPOL = SPI_CPOL_High;
    init.SPI_CPHA = SPI_CPHA_2Edge;
    init.SPI_NSS = SPI_NSS_Soft;                           // 软件NSS
    init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;  // 36MHz/8=4.5MHz
    init.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init (AD_SPI_PORT, &init);
    SPI_Cmd (AD_SPI_PORT, ENABLE);

    GPIO_InitTypeDef pinit = {0};
    pinit.GPIO_Pin = AD_PORTB_AFPP_PINS;
    pinit.GPIO_Mode = GPIO_Mode_AF_PP;
    pinit.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (AD_PORTB, &pinit);
}

void AD_HardReset (void) {
    // 复位脉冲（高电平有效）
    AD_Reset (1);
    Delay_Us (20);  // 复位脉冲宽度>10ns
    AD_Reset (0);
    Delay_Ms (5);   // 等待复位完成
}

void AD_PowerCtrl (FunctionalState state) {
    if (state == ENABLE) {
        AD_DRDir (0);
        AD_DRHold (0);
        AD_Update (0);
        AD_Profile (0);
        AD_PWR (1);
        Delay_Ms (20);  // 等待电源稳定
        AD_PWR (0);
        Delay_Ms (20);  // 等待电源稳定
    } else {
        AD_PWR (1);
    }
}

u8 spiReadWriteu8 (SPI_TypeDef *spi, u8 data) {

    while (0 == (spi->STATR & SPI_STATR_TXE));
    spi->DATAR = data;
    while (0 == (spi->STATR & SPI_STATR_RXNE));
    return spi->DATAR;
}

static void WriteReg (u8 addr, const void *data, u16 len) {
    AD_CS (0);
    u8 *ptr = (u8 *)data + len - 1;
    spiReadWriteu8 (AD_SPI_PORT, addr);

    for (u16 i = 0; i < len; i++) {
        spiReadWriteu8 (AD_SPI_PORT, *ptr--);
    }
    AD_UPDATE();
    AD_CS (1);
}

static void WriteRegSlow (u8 addr, const void *data, u16 len) {
    AD_CS (0);
    u8 *ptr = (u8 *)data + len - 1;
    spiReadWriteu8 (AD_SPI_PORT, addr);

    for (u16 i = 0; i < len; i++) {
        spiReadWriteu8 (AD_SPI_PORT, *ptr--);
    }
    AD_Update (1);
    Delay_Ms (1);
    AD_Update (0);
    AD_CS (1);
}

static void ReadReg (u8 addr, void *data, u32 len) {
    AD_CS (0);
    addr |= 0x80;
    u8 *ptr = (u8 *)data + len - 1;
    spiReadWriteu8 (AD_SPI_PORT, addr);
    for (s8 i = 0; i < len; i++) {
        *ptr-- = spiReadWriteu8 (AD_SPI_PORT, 0);
    }
    AD_CS (1);
}

void AD_StopAll (void) {
    __disable_irq();
    EXTI10_Disable();
    __enable_irq();
    // 开启SDIO,反Sinc滤波
    WriteReg (CFR1, &c1, 4);
    // 关闭SYNC_CLK,关闭PDCLK
    WriteReg (CFR2, &c2, 4);
}

static void ExtiInit (void) {
    EXTI_InitTypeDef EXTI_InitStructure = {0};
    NVIC_InitTypeDef NVIC_InitStructure = {0};
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO, ENABLE);
    GPIO_EXTILineConfig (GPIO_PortSourceGPIOA, GPIO_PinSource10);
    EXTI_InitStructure.EXTI_Line = EXTI_Line10;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init (&EXTI_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init (&NVIC_InitStructure);
}

void AD_Init (void) {
    // IO初始化
    IO_Init();
    // 初始化外部中断
    ExtiInit();
    // SPI初始化
    Spi_Init();
    // 开启AD电源
    AD_PowerCtrl (ENABLE);
    // 硬件复位
    AD_HardReset();
    // 设置时钟 40Mhz 25倍频，ICP384uA
    WriteRegSlow (CFR3, &c3, 4);
    // 等PLL锁定

    while (!AD_PLLLocked());
    // 开启SDIO,反Sinc滤波
    WriteReg (CFR1, &c1, 4);
    // 关闭SYNC_CLK,关闭PDCLK
    WriteReg (CFR2, &c2, 4);
}

u32 AmplitudeCal (u32 amp) {
    amp = amp > 1000 ? 1000 : amp;
    u32 ireq = (amp << 8) / 50;
    u32 dacctl = 0;
    u32 result;
    if (ireq > (u32)(8.64 * 256)) {
        dacctl = ireq * 96 / (u32)(8.64 * 256) - 96;
        result = 0x3fff;
    } else {
        result = ireq * 0x3fff / (u32)(8.64 * 256);
    }
    WriteReg (DACCTL, &dacctl, 4);
    return result;
}

u32 AmplitudeCal20mA (float amp) {
    amp = amp > 1000 ? 1000 : amp;
    float ireq = amp / 50;
    return ireq * 0x3fff / 20;
}

void AD_GenerateSinWave (u32 amp, u32 FTW) {
    PROFILE prof = {0};
    prof.ampScale = AmplitudeCal (amp);
    prof.freqTuning = FTW;
    WriteReg (CFR1, &c1, 4);
    WriteReg (PROF0, &prof, sizeof (PROFILE));
    TCFR2 lc2 = c2;
    lc2.SingleTone = ENABLE;
    WriteReg (CFR2, &lc2, 4);
}

void AD_BodeScan (u32 amp, u32 FTW, PPROFILE prof) {
    if (prof->ampScale == 0) {
        prof->ampScale = AmplitudeCal (amp);
        prof->freqTuning = FTW;
        WriteReg (PROF0, prof, sizeof (PROFILE));
        TCFR2 lc2 = c2;
        lc2.SingleTone = ENABLE;
        WriteReg (CFR2, &lc2, 4);
        WriteReg (CFR1, &c1, 4);
    } else {
        prof->freqTuning = FTW;
        WriteReg (PROF0, prof, sizeof (PROFILE));
    }
}

void AD_BodeStop (PPROFILE prof) {
    prof->ampScale = 0;
    prof->freqTuning = 0;
    prof->phaseOffset = 0;
    WriteReg (PROF0, prof, sizeof (PROFILE));
    WriteReg (CFR2, &c2, 4);
    WriteReg (CFR1, &c1, 4);
}

typedef u32 (*RamDataGenerator) (u32 amp, u16 *idx, u16 bound, u16 len);
static u32 phase90 = (u32)(0 * 65535 / 360) << 16;
static u32 phase0 = (u32)(90 * 65535 / 360) << 16;
static u32 phase270 = (u32)(180 * 65535 / 360) << 16;

u32 SquareDataGenerator (u32 amp, u16 *idx, u16 bound, u16 len) {

    if (*idx < bound) {
        return amp | phase90;
    } else {
        return amp | phase270;
    }
}

u32 Triangularwave (u32 amp, u16 *idx, u16 bound, u16 len) {
    u32 a = amp << 1;
    a = (*idx < bound) ? (a * (*idx)) / bound : (a * (len - *idx)) / (len - bound);
    a &= ~(3);
    if (a > amp) {
        a -= amp;
        return a | phase90;
    } else {
        a = amp - a;
        return a | phase270;
    }
    return roundf (a);
}

void txRam (RamDataGenerator generator, u32 value, u16 len, u32 bound) {
    AD_CS (0);
    spiReadWriteu8 (AD_SPI_PORT, RAM);
    u16 idx = 0;
    for (u16 i = 0; i < len; i++) {
        u32 data = generator (value, &idx, bound, len);
        idx++;
        u8 *p = (u8 *)(&data) + 3;
        for (u8 k = 0; k < 4; k++) {
            spiReadWriteu8 (AD_SPI_PORT, *p--);
        }
    }
    AD_CS (1);
    Delay_Ms (1);
}

void AD_GenerateSquareWave (u32 amp, u32 period, u32 pulse) {
    TCFR1 lc1 = c1;
    RAMPROFILE rpf = {0};
    u32 t1 = period / 4;
    rpf.step = (t1 + 1023) >> 10;
    u16 cnt = t1 / rpf.step;
    rpf.startaddr = 0;
    rpf.endaddr = cnt - 1;
    u16 duty = pulse / 4 / rpf.step;
    rpf.ramMode = Continuous_Recirculate;

    WriteReg (PROF0, &rpf, 8);
    u32 destamp = AmplitudeCal (amp) << 2;
    txRam (SquareDataGenerator, destamp, cnt, roundf (duty));
    lc1.RAM_Enable = ENABLE;
    lc1.playback_dest = Polar;
    lc1.AutoclearPhaseAccumulator = 1;
    WriteReg (CFR1, &lc1, 4);
}

u32 DblSquareDataGenerator (u32 amp, u16 *idx, u16 bound, u16 len) {
    u16 center = len >> 1;
    u16 p1 = 0;
    u16 p2 = bound;
    u16 p3 = p1 + center;
    u16 p4 = p2 + center;
    if (*idx >= p1 && *idx <= p2)
        return amp | phase90;
    else if (*idx >= p3 && *idx <= p4) {
        return amp | phase270;
    } else {
        return amp | phase0;
    }
}

void AD_GenerateDblSquareWave (u32 amp, u32 period, u32 pulse) {
    TCFR1 lc1 = c1;
    RAMPROFILE rpf = {0};
    u32 t1 = period / 4;
    rpf.step = (t1 + 1023) >> 10;
    u16 cnt = t1 / rpf.step;
    rpf.startaddr = 0;
    rpf.endaddr = cnt - 1;
    u16 duty = pulse / 4 / rpf.step;
    rpf.ramMode = Continuous_Recirculate;

    WriteReg (PROF0, &rpf, 8);
    u32 destamp = AmplitudeCal (amp) << 2;
    txRam (DblSquareDataGenerator, destamp, cnt, roundf (duty));
    lc1.RAM_Enable = ENABLE;
    lc1.playback_dest = Polar;
    lc1.AutoclearPhaseAccumulator = 1;
    WriteReg (CFR1, &lc1, 4);
}

void AD_GenerateTriangularwave (u32 amp, u32 period, u32 pulse) {

    TCFR1 lc1 = c1;
    RAMPROFILE rpf = {0};
    u32 t1 = period / 4;
    rpf.step = (t1 + 1023) >> 10;
    u16 cnt = t1 / rpf.step;
    rpf.startaddr = 0;
    rpf.endaddr = cnt - 1;
    u16 duty = pulse / 4 / rpf.step;
    rpf.ramMode = Continuous_Recirculate;


    WriteReg (PROF0, &rpf, 8);
    u32 destamp = AmplitudeCal20mA (amp) << 2;
    txRam (Triangularwave, destamp, cnt, roundf (duty));
    lc1.RAM_Enable = ENABLE;
    lc1.playback_dest = Polar;
    lc1.AutoclearPhaseAccumulator = 1;
    WriteReg (CFR1, &lc1, 4);
}

void EXTI15_10_IRQHandler (void) __attribute__ ((interrupt ("WCH-Interrupt-fast")));

void singleContinuousScan (void) {
    AD_DRTrig();
    Delay_Us (1);
    AD_DRTrig();
}

void biContinuousScan (void) {
    if (AD_DRState()) {
        AD_DRDir (0);
    } else {
        EXTI10_Disable();
    }
}

void (*volatile ScanInterrupt) (void) = NULL;

void EXTI15_10_IRQHandler (void) {
    if (EXTI_GetITStatus (EXTI_Line10) != RESET) {
        if (ScanInterrupt)
            ScanInterrupt();
        EXTI_ClearITPendingBit (EXTI_Line10);
    }
}

void EXTI10_Enable() {
    EXTI->INTFR |= EXTI_INTF_INTF10;
    NVIC_EnableIRQ (EXTI15_10_IRQn);
}

void EXTI10_Disable() {
    NVIC_DisableIRQ (EXTI15_10_IRQn);
    EXTI->INTFR |= EXTI_INTF_INTF10;
}

void AD_Scan_Up (u32 amp, u32 start, u32 end, u32 upstep, u16 upslope, u8 repeat) {
    TCFR2 cfr2 = c2;
    WriteReg (CFR2, &cfr2, sizeof (TCFR2));
    AD_DRDir (0);
    PROFILE pf = {0};
    pf.ampScale = AmplitudeCal (amp);
    WriteReg (PROF0, &pf, sizeof (PROFILE));
    WriteReg (CFR1, &c1, sizeof (TCFR1));

    TRampLimit limit;
    limit.Lower = ((u64)start << 32) / SYS_CLK;
    limit.Upper = ((u64)end << 32) / SYS_CLK;
    WriteReg (RampLimit, &limit, sizeof (TRampLimit));

    TRampStep lstep;
    lstep.incStep = ((u64)upstep << 32) / SYS_CLK;
    lstep.decStep = ((u64)upstep << 32) / SYS_CLK;
    WriteReg (RampStep, &lstep, sizeof (TRampStep));

    TRampSlope lslope;
    lslope.negSlope = upslope >> 2;
    lslope.posSlope = upslope >> 2;
    WriteReg (RampRate, &lslope, sizeof (TRampSlope));
    cfr2.SingleTone = DISABLE;
    cfr2.rampDest = RAMPDEST_FREQUENCY;
    cfr2.rampDwellHigh = ENABLE;
    cfr2.rampDwellLow = DISABLE;
    cfr2.rampEnable = 1;

    WriteReg (CFR2, &cfr2, sizeof (TCFR2));
    if (repeat) {
        while (AD_DROver() == 0);
        ScanInterrupt = singleContinuousScan;
        EXTI10_Enable();
    }
    AD_DRDir (1);
}

void AD_Scan_Down (u32 amp, u32 start, u32 end, u32 downstep, u16 downslope, u8 repeat) {
    TCFR2 cfr2 = c2;
    WriteReg (CFR2, &cfr2, sizeof (TCFR2));
    AD_DRDir (1);
    PROFILE pf = {0};
    pf.ampScale = AmplitudeCal (amp);
    WriteReg (PROF0, &pf, sizeof (PROFILE));
    WriteReg (CFR1, &c1, sizeof (TCFR1));

    TRampLimit limit;
    limit.Lower = ((u64)start << 32) / SYS_CLK;
    limit.Upper = ((u64)end << 32) / SYS_CLK;
    WriteReg (RampLimit, &limit, sizeof (TRampLimit));

    TRampStep lstep;
    lstep.incStep = ((u64)downstep << 32) / SYS_CLK;
    lstep.decStep = ((u64)downstep << 32) / SYS_CLK;
    WriteReg (RampStep, &lstep, sizeof (TRampStep));

    TRampSlope lslope;
    lslope.negSlope = downslope >> 2;
    lslope.posSlope = downslope >> 2;
    WriteReg (RampRate, &lslope, sizeof (TRampSlope));

    cfr2.SingleTone = DISABLE;
    cfr2.rampDest = RAMPDEST_FREQUENCY;
    cfr2.rampDwellHigh = DISABLE;
    cfr2.rampDwellLow = ENABLE;
    cfr2.rampEnable = 1;

    WriteReg (CFR2, &cfr2, sizeof (TCFR2));
    if (repeat) {
        while (AD_DROver() == 0);
        ScanInterrupt = singleContinuousScan;
        EXTI10_Enable();
    }
    AD_DRDir (0);
}

void AD_Scan_UpDown (u32 amp, u32 start, u32 end, u32 downstep, u16 downslope, u8 repeat) {
    TCFR2 cfr2 = c2;
    WriteReg (CFR2, &cfr2, sizeof (TCFR2));
    AD_DRDir (0);
    PROFILE pf = {0};
    pf.ampScale = AmplitudeCal (amp);
    WriteReg (PROF0, &pf, sizeof (PROFILE));
    WriteReg (CFR1, &c1, sizeof (TCFR1));

    TRampLimit limit;
    limit.Lower = ((u64)start << 32) / SYS_CLK;
    limit.Upper = ((u64)end << 32) / SYS_CLK;
    WriteReg (RampLimit, &limit, sizeof (TRampLimit));

    TRampStep lstep;
    lstep.incStep = ((u64)downstep << 32) / SYS_CLK;
    lstep.decStep = ((u64)downstep << 32) / SYS_CLK;
    WriteReg (RampStep, &lstep, sizeof (TRampStep));

    TRampSlope lslope;
    lslope.negSlope = downslope >> 2;
    lslope.posSlope = downslope >> 2;
    WriteReg (RampRate, &lslope, sizeof (TRampSlope));

    cfr2.SingleTone = DISABLE;
    cfr2.rampDest = RAMPDEST_FREQUENCY;
    cfr2.rampDwellHigh = repeat ? ENABLE : DISABLE;
    cfr2.rampDwellLow = repeat ? ENABLE : DISABLE;
    cfr2.rampEnable = 1;

    WriteReg (CFR2, &cfr2, sizeof (TCFR2));
    if (!repeat) {
        while (AD_DROver() == 0);
        ScanInterrupt = biContinuousScan;
        EXTI10_Enable();
    }
    AD_DRDir (1);
}